package top.lshaci.learning.springboot.jpaquerydsl.core;

import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import com.querydsl.core.types.Expression;
import com.querydsl.core.types.Path;
import com.querydsl.core.types.QBean;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.core.types.dsl.EntityPathBase;
import com.querydsl.jpa.impl.JPAQuery;
import com.querydsl.jpa.impl.JPAQueryFactory;
import lombok.Setter;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * DslUtil
 *
 * @author liangxu
 */
public class DslUtil {

    @Setter
    private static JPAQueryFactory jpaQueryFactory;

    /**
     * 构建 JPA 查询对象
     *
     * @param dslQuery dsl 查询对象
     * @param <V>      返回数据类型
     * @param <Q>      查询实体
     * @return JPA查询对象
     */
    public static <V, Q extends EntityPathBase<?>> JPAQuery<V> build(DslQuery dslQuery) {
        Q fromObj = dslQuery.fromObj();
        return build(dslQuery, (Expression<V>) fromObj, fromObj);
    }

    /**
     * 构建 JPA 查询对象
     *
     * @param dslQuery dsl 查询对象
     * @param voClass  返回数据类型
     * @param <V>      返回数据类型
     * @param <Q>      查询实体类型
     * @return JPA查询对象
     */
    public static <V, Q extends EntityPathBase<?>> JPAQuery<V> build(DslQuery dslQuery, Class<V> voClass) {
        QBean<V> select = DslResultUtil.select(voClass);
        Q from = dslQuery.fromObj();
        return build(dslQuery, select, from);
    }

    /**
     * 构建 JPA 查询对象
     *
     * @param fromObj  查询对象
     * @param dslQuery dsl 查询对象
     * @param <V>      返回数据类型
     * @param <F>      查询对象类型
     * @return JPA 查询对象
     */
    public static <V, F extends EntityPathBase<?>> JPAQuery<V> build(F fromObj, DslQuery dslQuery) {
        return build(dslQuery, (Expression<V>) fromObj, fromObj);
    }

    /**
     * 构建 JPA 查询对象
     *
     * @param dslQuery dsl 查询对象
     * @param voClass  返回数据类型
     * @param fromObj  查询对象
     * @param <V>      返回数据类型
     * @param <F>      查询对象类型
     * @return JPA 查询对象
     */
    public static <V, F extends EntityPathBase<?>> JPAQuery<V> build(DslQuery dslQuery, Class<V> voClass, F fromObj) {
        QBean<V> select = DslResultUtil.select(voClass);
        return build(dslQuery, select, fromObj);
    }

    /**
     * 构建 JPA 查询对象
     *
     * @param dslQuery dsl 查询对象
     * @param select   查询列
     * @param fromObj  查询对象
     * @param <V>      返回数据类型
     * @param <F>      查询对象类型
     * @return JPA 查询对象
     */
    private static <V, F extends EntityPathBase<?>> JPAQuery<V> build(DslQuery dslQuery, Expression<V> select, F fromObj) {
        Field[] fields = ReflectUtil.getFields(dslQuery.getClass());
        JPAQuery<V> jpaQuery = jpaQueryFactory.select(select);
        jpaQuery.from(fromObj);
        for (Field field : fields) {
            Object fieldValue = ReflectUtil.getFieldValue(dslQuery, field);
            if (ObjectUtil.isEmpty(fieldValue)) {
                continue;
            }
            BooleanExpression multiBe = multiDslCondition(field, fieldValue, fromObj);
            if (multiBe != null) {
                jpaQuery.where(multiBe);
                continue;
            }
            BooleanExpression singleBe = singleDslCondition(field, fieldValue, fromObj);
            if (singleBe != null) {
                jpaQuery.where(singleBe);
            }
        }
        return jpaQuery;
    }

    /**
     * 单个查询实体的Dsl查询条件
     *
     * @param field      查询字段
     * @param fieldValue 查询字段的值
     * @param fromObj    查询实体
     * @param <F>        查询实体类型
     * @return 单个查询实体的Dsl查询条件
     */
    private static <F extends EntityPathBase<?>> BooleanExpression singleDslCondition(Field field, Object fieldValue, F fromObj) {
        DslCondition dslCondition = field.getAnnotation(DslCondition.class);
        if (dslCondition != null && dslCondition.ignore()) {
            return null;
        }
        DslQueryField dslQueryField = dslQueryField(field, fieldValue, fromObj, dslCondition);
        return dslQueryField == null ? null : dslQueryField.toBooleanExpression();
    }

    /**
     * 多个查询实体的Dsl查询条件
     *
     * @param field      查询字段
     * @param fieldValue 查询字段的值
     * @param fromObj    查询实体
     * @param <F>        查询实体类型
     * @return 多个查询实体的Dsl查询条件
     */
    private static <F extends EntityPathBase<?>> BooleanExpression multiDslCondition(Field field, Object fieldValue, F fromObj) {
        DslConditions dslConditions = field.getAnnotation(DslConditions.class);
        if (dslConditions == null) {
            return null;
        }
        DslCondition[] dslConditionArr = dslConditions.value();
        if (ArrayUtil.isEmpty(dslConditionArr)) {
            return null;
        }
        List<DslQueryField> dslQueryFields = new ArrayList<>();
        for (DslCondition dslCondition : dslConditionArr) {
            DslQueryField dslQueryField = dslQueryField(field, fieldValue, fromObj, dslCondition);
            if (dslQueryField != null) {
                dslQueryFields.add(dslQueryField);
            }
        }
        return dslQueryFields.stream()
                .map(DslQueryField::toBooleanExpression)
                .filter(Objects::nonNull)
                .reduce(BooleanExpression::or)
                .orElse(null);
    }

    /**
     * 构建一个DSL查询字段信息
     *
     * @param field        查询对象字段
     * @param fieldValue   查询字段值
     * @param fromObj      查询实体
     * @param dslCondition DSL查询条件
     * @param <F>          查询实体类型
     * @return DSL查询字段信息
     */
    private static <F extends EntityPathBase<?>> DslQueryField dslQueryField(Field field, Object fieldValue, F fromObj, DslCondition dslCondition) {
        DslConditionMatcher dslMatcher = DslMatcherUtil.handle(field, dslCondition);
        if (dslMatcher == null) {
            return null;
        }
        DslQueryField dslQueryField = DslQueryField.build(dslMatcher, fieldValue);
        Object actualFrom = fromObj(dslCondition, fromObj);
        String[] queryField = queryField(dslCondition, field);
        for (String queryFieldName : queryField) {
            Object path = ReflectUtil.getFieldValue(actualFrom, queryFieldName);
            dslQueryField.addPath((Path<?>) path);
        }
        return dslQueryField;
    }

    /**
     * 获取查询实体
     *
     * @param dslCondition   查询注解
     * @param defaultFromObj 默认的查询实体
     * @return 查询实体
     */
    private static Object fromObj(DslCondition dslCondition, Object defaultFromObj) {
        if (dslCondition == null || dslCondition.fromClass() == DslVoidEntity.class) {
            return defaultFromObj;
        }
        return DslFromUtil.fromObj(dslCondition.fromClass());
    }

    /**
     * 获取查询字段
     *
     * @param dslCondition 查询注解
     * @param field        默认的查询实体
     * @return 查询字段
     */
    private static String[] queryField(DslCondition dslCondition, Field field) {
        if (dslCondition == null || ArrayUtil.isEmpty(dslCondition.field())) {
            return new String[]{field.getName()};
        }
        return dslCondition.field();
    }

}
